﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Collections.Generic;
using Liquifier;

namespace LiquifierTest
{
    /// <summary>
    ///This is a test class for LiquiferTest and is intended
    ///to contain all LiquiferTest Unit Tests
    ///</summary>
    public partial class LiquiferAttributesTest
    {

        #region Type Interop Test

        [Serializable]
        public class SerializeableClass
        {
            public int serializedValue;
            public SerializeableClass secondGenerationSerialisedClass;
        }

        [Liquifiable]
        public class LiquifiableClass
        {
            public SerializeableClass serialisableClass;
            public SerializeableClass serialisableClassSecondInstance;

            public LiquifiableClass serialisebleClassContainer;

            public List<SerializeableClass> arrayOfSerialisableClass = new List<SerializeableClass>();
            public List<LiquifiableClass> arrayOfLiquifiableClass = new List<LiquifiableClass>();
        }

        [TestMethod()]
        public void AreSerializableObjectsPreserved()
        {
            SerializeableClass serializeable = new SerializeableClass();
            SerializeableClass secondgenserializeable = new SerializeableClass();

            LiquifiableClass before = new LiquifiableClass();

            serializeable.serializedValue = 1;
            serializeable.secondGenerationSerialisedClass = secondgenserializeable;
            secondgenserializeable.serializedValue = 2;
            secondgenserializeable.secondGenerationSerialisedClass = null;  //no circular references here: BinaryFormatter cannot handle it!

            before.serialisableClass = serializeable;  //Liquifier should reuse objects however, even if they are not serializable (at the top level)
            before.serialisableClassSecondInstance = serializeable;

            LiquifiableClass secondLiquifiable = new LiquifiableClass();
            secondLiquifiable.serialisableClass = serializeable;

            before.arrayOfLiquifiableClass.Add(secondLiquifiable); //it should also do it with arrays
            before.arrayOfSerialisableClass.Add(serializeable);

            LiquifiableClass after = DoLiquifify(before);

            Assert.AreEqual(1, after.serialisableClass.serializedValue);
            Assert.AreEqual(2, after.serialisableClass.secondGenerationSerialisedClass.serializedValue);

            Assert.AreSame(after.serialisableClass, after.serialisableClassSecondInstance);

            Assert.AreSame(after.serialisableClass, after.arrayOfLiquifiableClass[0].serialisableClass);
            Assert.AreSame(after.arrayOfSerialisableClass[0], after.serialisableClass);

        }

        #endregion


        #region Non Blittable Types Test

        [Serializable]
        public struct NonBlittableType
        {
            public string MakesItNonBlittable;
            public int value;
        }

        [Liquifiable]
        public class NonBlittableContainer
        {
            public NonBlittableType item;
            public NonBlittableType[] items;
        }

        [TestMethod()]
        //[ExpectedException(typeof(Exception))]
        public void CanHandleNonBlittableTypes()
        {

            NonBlittableContainer before = new NonBlittableContainer();

            NonBlittableType nonblit = new NonBlittableType() { MakesItNonBlittable = "oh no!", value = 10 };

            before.item = nonblit;

            before.items = new NonBlittableType[10];

            before.items[0] = nonblit;
            before.items[1] = nonblit;
            before.items[2] = nonblit;

            try
            {
                NonBlittableContainer after = DoLiquifify(before);

                //since NonBlittableType is a value type it should not be being reused
                Assert.AreNotSame(after.item, after.items[0]);
                Assert.AreNotSame(after.item, after.items[1]);
                Assert.AreNotSame(after.items[1], after.items[2]);

                Assert.AreEqual("oh no!", after.item.MakesItNonBlittable);
                Assert.AreEqual("oh no!", after.items[0].MakesItNonBlittable);
                Assert.AreEqual("oh no!", after.items[1].MakesItNonBlittable);
                Assert.AreEqual("oh no!", after.items[2].MakesItNonBlittable);

                Assert.AreEqual(10, after.item.value);
            }
            catch (ArgumentException e)
            {
                Assert.Fail(e.Message);
            }
        }

         [TestMethod()]
        public void CanBeStressedWithNonBlittableTypes()
        {
            NonBlittableContainer before = new NonBlittableContainer();
            NonBlittableType nonblit = new NonBlittableType() { MakesItNonBlittable = "oh no!", value = 10 };

            before.item = nonblit;
            before.items = new NonBlittableType[100000];

            for (int i = 0; i < 100000; i++)
            {
                before.items[i] = nonblit;
            }

            NonBlittableContainer after = DoLiquifify(before);

            CollectionAssert.Contains(after.items, after.item);

        }


        #endregion

        #region ISerializeableBehaviourTesting

        [Liquifiable(Serialisation.Public | Serialisation.Private, Serialisation.Public)]
         public class SerContainerClass
         {
             public SerStruct fields;
             public SerStruct properts { get; set; }

             public SerClass fieldc;
             public SerClass propertc { get; set; }

         }

         [Serializable]
         public struct SerStruct
         {
             public string field;
             public string Property { get; set; }
         }

         [Serializable]
         public class SerClass
         {
             public string field;
             public string Property { get; set; }
         }

        /// <summary>
        /// The purpose of this test is to make sure that when [Serializeable] types are serialized, they behave as the user *expects* - see, Liquifier doesnt process backing
        /// fields but processes the properties themselves, so if it serializes a serializeable class itself, for whatever reason (e.g. new optimization) with the default 
        /// Serializeable settings, auto properties like the ones above Won't be serialized. This would be correct, but completely contrary to the user's expectation, so we
        /// need to recreate it.
        /// </summary>
        [TestMethod()]
        public void Are_Properties_Of_Serializeable_Types_Serialised()
        {
            SerContainerClass before = new SerContainerClass();

            before.fields = new SerStruct() { field = "field", Property = "property" };
            before.properts = before.fields;

            before.fieldc = new SerClass() { field = "field", Property = "property" };
            before.propertc = before.fieldc;

            SerContainerClass after = DoLiquifify(before);

            Assert.AreEqual("field", after.fields.field);
            Assert.AreEqual("property", after.fields.Property);

            Assert.AreEqual("field", after.properts.field);
            Assert.AreEqual("property", after.properts.Property);

            Assert.AreEqual("field", after.fieldc.field);
            Assert.AreEqual("property", after.fieldc.Property);

            Assert.AreEqual("field", after.propertc.field);
            Assert.AreEqual("property", after.propertc.Property);

        }

        #endregion


    }
}
